package ch.exm.storm.collections;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class WeakCollection<T> implements Collection<T> {
	
	private Collection<WeakReference<T>> innerCollection = new ArrayList<WeakReference<T>>();

	public boolean add(T o) {
		innerCollection.add(new WeakReference<T>(o));
		return true;
	}

	public boolean addAll(Collection<? extends T> c) {
		boolean modified = false;
		for (T o : c) modified = add(o) || modified;
		return modified;
	}

	public void clear() {
		innerCollection.clear();
	}

	public boolean contains(Object o) {
		boolean result = false;
		Collection<WeakReference<T>> nullRefs = new ArrayList<WeakReference<T>>();
		
		for (WeakReference<T> wr : innerCollection) {
			if (wr == null) continue;
			T ref = wr.get();
			if (ref == null) nullRefs.add(wr);
			else if (ref.equals(o)) result = true;
		}
		
		innerCollection.removeAll(nullRefs);
		return result;
	}

	public boolean containsAll(Collection<?> c) {
		for (T o : (Collection<T>)c) {
			if (!contains(o)) return false;
		}
		return true;
	}

	public boolean isEmpty() {
		return innerCollection.isEmpty();
	}

	public Iterator<T> iterator() {
		return toStrongRefs().iterator();
	}

	public boolean remove(Object o) {
		boolean modified = false;
		Collection<WeakReference<T>> refsToRemove = new ArrayList<WeakReference<T>>();
		
		for (WeakReference<T> wr : innerCollection) {
			if (wr == null) continue;
			T ref = wr.get();
			if (ref == null) {
				refsToRemove.add(wr);
			} else if (ref.equals(o)) {
				refsToRemove.add(wr);
				modified = true;
			}
		}
		
		innerCollection.removeAll(refsToRemove);
		return modified;
	}

	public boolean removeAll(Collection<?> c) {
		boolean modified = false;
		for (T o : (Collection<T>)c) modified = remove(o) || modified;
		return modified;
	}

	public boolean retainAll(Collection<?> c) {
		boolean modified = false;
		Collection<WeakReference<T>> refsToRemove = new ArrayList<WeakReference<T>>();
		
		for (WeakReference<T> wr : innerCollection) {
			T ref = wr.get();
			if (ref == null) {
				refsToRemove.add(wr);
			} else if (!c.contains(ref)) {
				refsToRemove.add(wr);
				modified = true;
			}
		}
		
		innerCollection.removeAll(refsToRemove);
		return modified;
	}

	public int size() {
		return innerCollection.size();
	}

	public Object[] toArray() {
		return toStrongRefs().toArray();
	}

	public <T> T[] toArray(T[] a) {
		return toStrongRefs().toArray(a);
	}

	private Collection<T> toStrongRefs() {
		Collection<WeakReference<T>> nullRefs = new ArrayList<WeakReference<T>>();
		Collection<T> strongRefs = new ArrayList<T>();

		for (WeakReference<T> wr : innerCollection) {
			T ref = wr.get();
			if (ref == null) nullRefs.add(wr);
			else strongRefs.add(ref);
		}

		innerCollection.removeAll(nullRefs);
		return strongRefs;
	}
}
